import { expectType, expectNotType } from 'tsd'
type FuncStruct = (...args: any[]) => any

type FunctionKeys<T extends object> = {
  [K in keyof T]: T[K] extends FuncStruct ? K : never
}[keyof T]
type obj_test = {
  func: (name: string) => {}
  bar_func: (name: string[]) => {}
  name: string
}

// -- test
type FunctionKeys_test1 = FunctionKeys<obj_test>

//# RequiredKeys、OptionalKeys
//在属性修饰工具类型中我们只实现了 FunctionKeys，它的实现相对简单，因为只需要判断类型即可。那如果，我们要获取一个接口中所有可选或必选的属性呢？现在没法通过类型判断，要怎么去收集属性？

type Tmp1 = {} extends { prop: number } ? 'Y' : 'N' // "N"
type Tmp2 = {} extends { prop?: number } ? 'Y' : 'N' // "Y"
//$ 此时 TypeScript 会使用基于结构化类型的比较，也就意味着由于 { prop: number } 可以视为继承自 {} ，{} extends { prop: number } 是不满足条件的。但是，如果这里的 prop 是可选的，那就不一样了！由于 { prop?: number } 也可以是一个空的接口结构，那么 {} extends { prop?: number } 就可以认为是满足的。

//因此，我们可以这么实现RequireKeys：
export type RequireKeys<T extends object> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? never : K
}[keyof T]

// -- test
type RequireKeys_objTest = {
  func: (name: string) => {}
  bar_func: (name: string[]) => {}
  name: string
  age?: number
}

type RequireKeys_res1 = RequireKeys<RequireKeys_objTest> //"func" | "bar_func" | "name"

// OptionalKeys 也是类似：
export type OptionalKeys<T extends object> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never
}[keyof T]
//--test
type OptionalKeys_objTest = {
  func: (name: string) => {}
  bar_func: (name: string[]) => {}
  name: string
  age?: number
  optional?: boolean
}
type OptionalKeys_res1 = OptionalKeys<OptionalKeys_objTest> // "age" | "optional"

// # MutableKeys、ImmutableKeys
// MutableKeys 和 ImmutableKeys 则要更加复杂一些，因为 readonly 修饰符无法简单地通过结构化类型比较，我们需要一个能对只读这一特性进行判断的辅助工具类型，直接看例子再讲解：
type Equal<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B

// 在这里，<T>() => T extends X ? 1 : 2 和 <T>() => T extends Y ? 1 : 2 这两个函数结构实际上起辅助作用，内部的条件类型并不会真的进行运算。我们实际上是借助这一辅助结构判断类型 X 与 Y 的全等性，这一全等性就包括了 readonly 修饰符与可选性等。
type Equal_obj1 = {
  readonly name?: string
}
type Equal_obj2 = {
  readonly name?: string
}
type isEqual_res1 = Equal<Equal_obj1, Equal_obj2>

// $ MutableKeys 不要readony属性修饰符的key
export type MutableKeys<T extends object> = {
  [P in keyof T]-?: Equal<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P, never>
}[keyof T]
//--test
type MutableKeys_objTest = {
  readonly func: (name: string) => {}
  name: string
  age?: number
}
type MutableKeys_res1 = MutableKeys<MutableKeys_objTest> // "name" | "age"

// $ ImmutableKeys  只要readony属性修饰符的key
export type ImmutableKeys<T extends object> = {
  [P in keyof T]-?: Equal<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
}[keyof T]
// -- test
type ImmutableKeys_objTest = {
  readonly func: (name: string) => {}
  name: string
  age?: number
}
type ImmutableKeys_res1 = ImmutableKeys<ImmutableKeys_objTest> // "func"

expectType<ImmutableKeys<{ a: string; readonly b: string }>>('b')
expectNotType<ImmutableKeys<{ a: string; readonly b: string }>>('a')
